package starComponent.elements;

import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;

import starComponent.E_PossibleBranches;


/**
 * @author Charles Perin
 * 11 avr. 2011
 * LIMSI - INRIA Rennes
 * charles.perin@free.fr
 */
public class A_StarBranch implements Comparable<A_StarBranch> {

	private static final long serialVersionUID = -6312879105601954483L;

	public static final int BOUNDING_STEP = 2, BOUNDING_SIDE = 100, BOUNDING_MARGIN=10;

	protected StarComponent star;

	private double min, max;

	private String name;

	protected double length;

	protected double xStart;

	protected double yStart;

	protected double xEnd;

	protected double yEnd;

	protected double angle;

	private Color branchColor;

	protected Ladder ladder;

	protected ArrayList<Rectangle> boundingList = new ArrayList<Rectangle>();

	private FlyingNameLabel flyingNameLabel;

	private boolean isSnapToTicks = false;

	protected Point middle = new Point();

	private boolean isBranchSelected = false;

	private boolean isPopupOpen = false;

	private PopupBranch popupBranch;

	private boolean isDisplayed = false;

	protected E_PossibleBranches branchType;


	public A_StarBranch(StarComponent star, E_PossibleBranches branch, Color color, Ladder ladder, double angle, boolean isDisplayed){
		this.ladder = ladder;
		this.branchType = branch;
		this.angle = angle;
		this.setFlyingNameLabel(new FlyingNameLabel(this));
		this.branchColor = color;
		this.popupBranch = new PopupBranch(this);
		this.star = star;
		this.name = branch.getName();
		this.min = branch.getMinValue();
		this.max = branch.getMaxValue();
		this.isDisplayed = isDisplayed;
		this.setSnapToTicks(branch.isSnapToTicks());
	}

	public void updateBranch(){
		double internCircleRadius = star.getControler().getInternCircleRadius();
		double externCircleRadius = star.getControler().getExternCircleRadius();
		double xMiddle = star.getWidth()/2;
		double yMiddle = star.getHeight()/2;
		xStart = internCircleRadius*Math.cos(angle)+xMiddle;
		yStart = -internCircleRadius*Math.sin(angle)+yMiddle;
		xEnd = externCircleRadius*Math.cos(angle)+xMiddle;
		yEnd = -externCircleRadius*Math.sin(angle)+yMiddle;
		length = Math.sqrt(Math.pow(Math.abs(xStart-xEnd),2)+Math.pow(Math.abs(yStart-yEnd),2));
		middle.setLocation((xEnd-xStart)/2, (yEnd-yStart)/2);
		ladder.updateLadderMap(this);
		buildBoundingBox();

		if(this instanceof StarBranchSlider){
			StarBranchSlider slider = (StarBranchSlider)this;
			slider.getThumb().initPosition();
		}
		else if(this instanceof StarBranchBiSlider){
			StarBranchBiSlider biSlider = (StarBranchBiSlider)this;
			biSlider.getThumbStart().initPosition();
			biSlider.getThumbEnd().initPosition();
		}
		else if(this instanceof StarBranchBinaries){
			StarBranchBinaries binaryBranch = (StarBranchBinaries)this;
			for(ThumbForMultipleBinaries t:binaryBranch.getThumbsTab()){
				t.initPosition();
			}
		}
		this.getFlyingNameLabel().updateText();


	}

	public boolean pointIsInBranch(int x, int y){
		for(Rectangle r:boundingList){
			if(r.contains(x, y))return true;
		}
		return false;
	}

	protected void buildBoundingBox(){

		double x0,y0,x1,y1;
		x0 =  star.getCenter().getX();
		x1 =  xEnd;
		y0 =  star.getCenter().getY();
		y1 =  yEnd;

		double y0Backup=y0;
		double x0Backup=x0;

		boolean leftToRight = xStart<=xEnd;
		boolean downToTop = yStart<=yEnd;

		//Si construction par rectangles horizontaux
		if(angle>Math.PI/4 && angle<3*Math.PI/4){
			if(leftToRight){
				while(x0<=x1+BOUNDING_MARGIN && y0>=y1-BOUNDING_MARGIN){
					Rectangle r = new Rectangle((int)(x0-BOUNDING_SIDE/2), (int)y0, BOUNDING_SIDE, BOUNDING_STEP);
					x0 = (int) (-(y0-y0Backup)/Math.tan(angle)+x0Backup);
					y0 -= BOUNDING_STEP;

					boundingList.add(r);
				}
			}
			else{
				while(x0>=x1-BOUNDING_MARGIN && y0>=y1-BOUNDING_MARGIN){
					Rectangle r = new Rectangle((int)(x0-BOUNDING_SIDE/2), (int)y0, BOUNDING_SIDE, BOUNDING_STEP);
					x0 = (int) (-(y0-y0Backup)/Math.tan(angle)+x0Backup);
					y0 -= BOUNDING_STEP;

					boundingList.add(r);
				}
			}
		}
		else if(angle >5*Math.PI/4 && angle < 7*Math.PI/4){
			if(leftToRight){
				while(x0<=x1+BOUNDING_MARGIN && y0<=y1+BOUNDING_MARGIN){
					Rectangle r = new Rectangle((int)(x0-BOUNDING_SIDE/2), (int)y0, BOUNDING_SIDE, BOUNDING_STEP);
					x0 = (int) (-(y0-y0Backup)/Math.tan(angle)+x0Backup);
					y0 += BOUNDING_STEP;

					boundingList.add(r);
				}
			}
			else{
				while(x0>=x1-BOUNDING_MARGIN && y0<=y1+BOUNDING_MARGIN){
					Rectangle r = new Rectangle((int)(x0-BOUNDING_SIDE/2), (int)y0, BOUNDING_SIDE, BOUNDING_STEP);
					x0 = (int) (-(y0-y0Backup)/Math.tan(angle)+x0Backup);
					y0 += BOUNDING_STEP;

					boundingList.add(r);
				}
			}
		}
		//si construction par rectangles verticaux
		else{
			if(leftToRight){
				if(downToTop){
					while(x0<=x1+BOUNDING_MARGIN && y0<=y1+BOUNDING_MARGIN){
						Rectangle r = new Rectangle((int)x0, (int)(y0-BOUNDING_SIDE/2), BOUNDING_STEP, BOUNDING_SIDE);
						x0+=BOUNDING_STEP;
						y0 = (-(x0-xStart)*Math.tan(angle)+yStart);

						boundingList.add(r);
					}
				}
				else{
					while(x0<=x1+BOUNDING_MARGIN && y0>=y1-BOUNDING_MARGIN){
						Rectangle r = new Rectangle((int)x0, (int)(y0-BOUNDING_SIDE/2), BOUNDING_STEP, BOUNDING_SIDE);
						x0+=BOUNDING_STEP;
						y0 = (-(x0-xStart)*Math.tan(angle)+yStart);

						boundingList.add(r);
					}
				}

			}
			else{
				if(downToTop){
					while(x0>=x1-BOUNDING_MARGIN && y0<=y1+BOUNDING_MARGIN){
						Rectangle r = new Rectangle((int)x0, (int)(y0-BOUNDING_SIDE/2), BOUNDING_STEP, BOUNDING_SIDE);
						x0-=BOUNDING_STEP;
						y0 = (-(x0-xStart)*Math.tan(angle)+yStart);

						boundingList.add(r);
					}
				}
				else{
					while(x0>=x1-BOUNDING_MARGIN && y0>=y1-BOUNDING_MARGIN){
						Rectangle r = new Rectangle((int)x0, (int)(y0-BOUNDING_SIDE/2), BOUNDING_STEP, BOUNDING_SIDE);
						x0-=BOUNDING_STEP;
						y0 = (-(x0-xStart)*Math.tan(angle)+yStart);

						boundingList.add(r);
					}
				}
			}
		}
	}


	public void selectBranch() {
		if(!isBranchSelected && !isPopupOpen){
			isBranchSelected = true;
		}
		else{
			isBranchSelected = false;
		}
		star.repaint();
	}

	public void releaseBranch() {
		if(isBranchSelected){
			isBranchSelected = false;
			star.repaint();
		}		
	} 

	public void openPopup(int x, int y) {
		if(!isPopupOpen){
			popupBranch = new PopupBranch(this, x, y);
			popupBranch.createPolygons();
			isPopupOpen = true;
		}
	}


	public void draggBranch(Point p) {
		if(isBranchSelected){
			double newAngle = -Math.atan2(p.y-star.getCenter().getY(), p.x-star.getCenter().getX());
			if(newAngle<0)newAngle = newAngle+2*Math.PI;
			angle = newAngle;
			updateBranch();
			star.getControler().orderBranchesByAngle();
		}
	}

	public void remove() {
		getStar().remove(flyingNameLabel);
		getStar().remove(popupBranch);
		if(this instanceof StarBranchSlider){
			getStar().remove(((StarBranchSlider)this).getThumb());
		}
		else if(this instanceof StarBranchBiSlider){
			getStar().remove(((StarBranchBiSlider)this).getThumbStart());
			getStar().remove(((StarBranchBiSlider)this).getThumbEnd());
		}
		else if(this instanceof StarBranchBinaries){
			for(ThumbForMultipleBinaries t: ((StarBranchBinaries)this).getThumbsTab()){
				getStar().remove(t);
			}
		}
	}


	public PopupBranch getPopupBranch() {
		return popupBranch;
	}

	public void setPopupBranch(PopupBranch popupBranch) {
		this.popupBranch = popupBranch;
	}


	public double getLength() {
		return length;
	}

	public void setLength(double length) {
		this.length = length;
	}

	public void setMin(double min) {
		this.min = min;
	}

	public double getMin() {
		return min;
	}

	public void setMax(double max) {
		this.max = max;
	}

	public double getMax() {
		return max;
	}

	public double getxStart() {
		return xStart;
	}

	public void setxStart(double xStart) {
		this.xStart = xStart;
	}

	public double getyStart() {
		return yStart;
	}

	public void setyStart(double yStart) {
		this.yStart = yStart;
	}

	public double getxEnd() {
		return xEnd;
	}

	public void setxEnd(double xEnd) {
		this.xEnd = xEnd;
	}

	public double getyEnd() {
		return yEnd;
	}

	public void setyEnd(double yEnd) {
		this.yEnd = yEnd;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setBranchColor(Color branchColor) {
		this.branchColor = branchColor;
	}

	public Color getBranchColor() {
		return branchColor;
	}

	public void setLadder(Ladder ladder) {
		this.ladder = ladder;
	}

	public Ladder getLadder() {
		return ladder;
	}

	public double getAngle() {
		return angle;
	}

	public void setAngle(double angle) {
		if(angle<0)angle+=2*Math.PI;
		this.angle = angle;
	}

	public void setFlyingNameLabel(FlyingNameLabel flyingNameLabel) {
		this.flyingNameLabel = flyingNameLabel;
	}

	public FlyingNameLabel getFlyingNameLabel() {
		return flyingNameLabel;
	}

	public StarComponent getStar() {
		return star;
	}

	public void setStar(StarComponent star) {
		this.star = star;
	}

	public void setSnapToTicks(boolean isSnapToTicks) {
		this.isSnapToTicks = isSnapToTicks;
	}

	public boolean isSnapToTicks() {
		return isSnapToTicks;
	}

	public Point getMiddle() {
		return middle;
	}

	public boolean isBranchSelected() {
		return isBranchSelected;
	}

	public void setBranchSelected(boolean isBranchSelected) {
		this.isBranchSelected = isBranchSelected;
	}

	public void setPopupOpen(boolean isPopupOpen) {
		this.isPopupOpen = isPopupOpen;
	}

	public boolean isPopupOpen() {
		return isPopupOpen;
	}

	@Override
	public int compareTo(A_StarBranch b) {
		Double a1 = new Double(this.angle);
		Double a2 = new Double(b.getAngle());
		if (a1.compareTo(a2) == 1) {
			return -1;
		} else if (a1.compareTo(a2) == -1) {
			return 1;        	
		} else {
			return 0;
		}
	}

	public void setDisplayed(boolean isDisplayed) {
		this.isDisplayed = isDisplayed;
	}

	public boolean isDisplayed() {
		return isDisplayed;
	}

	public E_PossibleBranches getBranchType() {
		return branchType;
	}

	public void setBranchType(E_PossibleBranches branchType) {
		this.branchType = branchType;
	}


}
